package com.yupi.springdata.springdataredis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * redis配置类
 * @author li
 */
@Configuration
// 确保启用事务管理
@EnableTransactionManagement
// 将订阅者加到spring容器里扫描订阅者组件，不加这个注解就在下面加bean
@ComponentScan("com.yupi.springdata.springdataredis")
@EnableRedisRepositories("com.yupi.springdata.springdataredis.repository")
public class RedisConfig {
    // 1.redis连接工厂
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // 设置远程单机redis服务地址
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("192.168.8.171", 6380);
        // 设置数据库index，默认0
        config.setDatabase(0);

        // 集群redis服务地址，多个远程地址的ip， new RedisClusterConfiguration()

        // 连接池相关配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大连接数（根据并发量估算）
        jedisPoolConfig.setMaxTotal(100);
        // 最大连接数 = 最大空闲数
        jedisPoolConfig.setMaxIdle(100);
        // 最小空闲连接数
        jedisPoolConfig.setMinIdle(10);
        // 最长等待时间 0无限等待，解决现成堆积问题最好设置
        jedisPoolConfig.setMaxWait(Duration.ofSeconds(2));
        // 在SDR2.0+，建议使用JedisClientConfiguration来设置连接池
        // 默认设置了读取超时和连接超时为2秒
        JedisClientConfiguration clientConfiguration = JedisClientConfiguration.builder().usePooling().poolConfig(jedisPoolConfig).build();

        return new JedisConnectionFactory(config, clientConfiguration);
    }

    // 2.redis模板类（工具类）
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 修改序列化类型，StringRedisSerializer只能传入String类型的值（存入redis之前就要转化成String）
//        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        //改成Jackson2JsonSerializer(这个会把redis的key加引号，占空间) 推荐
//        redisTemplate.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class));
        // 解决方式：把key设置成string的序列化器，把value设置成json序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        return redisTemplate;
    }

    // 3.支持spring声明式事务，与常规配置分开
    @Bean
    public RedisTemplate tranRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        // 支持spring声明式事务
        redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }

    // spring声明事务要加这个bean
    @Bean
    public PlatformTransactionManager platformTransactionManager() {
        return new AbstractPlatformTransactionManager() {
            @Override
            protected Object doGetTransaction() throws TransactionException {
                return new Object();
            }

            @Override
            protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {

            }

            @Override
            protected void doCommit(DefaultTransactionStatus status) throws TransactionException {

            }

            @Override
            protected void doRollback(DefaultTransactionStatus status) throws TransactionException {

            }
        };
    }

    // 推荐
    @Bean
    MessageListenerAdapter messageListenerAdapter(MessageListener messageListener) {
        return new MessageListenerAdapter(messageListener);
    }

    // 消息监听者容器，负责：管理线程池、消息分发、分发给对应管道的订阅者，配置在订阅者当中
    // 可以动态设置订阅者，可以在不重启订阅者服务器的情况下，动态设置订阅者和管道
    @Bean
    RedisMessageListenerContainer container(MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        // 设置连接工厂
        container.setConnectionFactory(redisConnectionFactory());
        // 设置监听者绑定的管道
        List<Topic> topicList = new ArrayList<>();
        topicList.add(new PatternTopic("log"));
        // 第一个参数是监听者，第二个参数是绑定的管道
        container.addMessageListener(messageListenerAdapter, topicList);
        return container;
    }
}
